#include "CDXMesh.h"
#include "CDXTextureManager.h"
#include "Safe_Utils.h"

//////////////////////////////////////////////////////////////////////////
IDXMesh::IDXMesh(UINT meshType /* = MESHTYPE_NULL */)
{
	m_ref = 0;
	m_ID = DXMESH_BAD_MESH_ID;
	m_isPrimitive = false;
	m_meshType = meshType;
}

IDXMesh::IDXMesh(const IDXMesh& rhs)
{
	if (strcmp(this->m_meshName, rhs.m_meshName) != 0)
	{
		this->m_isPrimitive = rhs.m_isPrimitive;
		this->m_ref = 1;
		strcpy_s(this->m_meshName, 16, rhs.m_meshName);
	}
}

IDXMesh::~IDXMesh()
{
}

IDXMesh& IDXMesh::operator=(const IDXMesh& rhs)
{
	if (strcmp(this->m_meshName, rhs.m_meshName) != 0)
	{
		strcpy_s(this->m_meshName, 16, rhs.m_meshName);
	}

	return *this;
}

void IDXMesh::DrawMesh(IDirect3DDevice9 *pDXDevice)
{
}

void IDXMesh::DeleteMesh()
{
}

//////////////////////////////////////////////////////////////////////////

CDXDirectXMesh::CDXDirectXMesh() : IDXMesh(IDXMesh::MESHTYPE_DX)
{
	m_mesh = NULL;
}

CDXDirectXMesh::CDXDirectXMesh(const CDXDirectXMesh& rhs)
{
	if (this->m_mesh != rhs.m_mesh)
	{
		this->m_mesh = rhs.m_mesh;
		this->m_materials = rhs.m_materials;
		this->m_textures = rhs.m_textures;
		this->m_isPrimitive = rhs.m_isPrimitive;
		this->m_ref = 1;
		strcpy_s(this->m_meshName, 16, rhs.m_meshName);
	}
}

CDXDirectXMesh::~CDXDirectXMesh()
{
	UnloadMesh();
}

CDXDirectXMesh& CDXDirectXMesh::operator=(const CDXDirectXMesh& rhs)
{
	if (this->m_mesh != rhs.m_mesh)
	{
		this->m_mesh = rhs.m_mesh;
		strcpy_s(this->m_meshName, 16, rhs.m_meshName);
	}

	return *this;
}

bool CDXDirectXMesh::LoadMesh(const char* FileName, IDirect3DDevice9* pDXDevice)
{
	HRESULT hr = 0;

	//
	// Load the XFile data.
	//

	ID3DXBuffer* adjBuffer  = 0;
	ID3DXBuffer* mtrlBuffer = 0;
	DWORD        numMtrls   = 0;

	hr = D3DXLoadMeshFromX(  
		(LPCSTR)FileName,
		D3DXMESH_MANAGED,
		pDXDevice,
		&adjBuffer,
		&mtrlBuffer,
		0,
		&numMtrls,
		&m_mesh);

	if(FAILED(hr))
	{
		MessageBox(0, DXGetError(hr), "D3DXLoadMeshFromX() - FAILED", 0);
		return false;
	}

	CDXTextureManager* pTM = CDXTextureManager::GetInstance();

	//
	// Extract the materials, and load textures.
	//

	if( mtrlBuffer != 0 && numMtrls != 0 )
	{
		D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer();

		for(unsigned int i = 0; i < numMtrls; i++)
		{
			// HACK ambient
			// the MatD3D property doesn't have an ambient value set
			// when its loaded, so set it now:
			mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse;

			// save the ith material
			m_materials.push_back( mtrls[i].MatD3D );

			// check if the ith material has an associative texture
			if( mtrls[i].pTextureFilename != 0 )
			{
				//// yes, load the texture for the ith subset
				//IDirect3DTexture9* tex = 0;
				//D3DXCreateTextureFromFile(
				//	Device,
				//	mtrls[i].pTextureFilename,
				//	&tex);


				// save the loaded texture
				m_textures.push_back( pTM->LoadTexture(mtrls[i].pTextureFilename) );
			}
			else
			{
				// no texture for the ith subset
				m_textures.push_back( CDXTextureManager::DXTEXTURE_NULL_INDEX );
			}
		}
	}
	//d3d::Release<ID3DXBuffer*>(mtrlBuffer); // done w/ buffer
	mtrlBuffer->Release();

	//
	// Optimize the mesh.
	//

	hr = m_mesh->OptimizeInplace(		
		D3DXMESHOPT_ATTRSORT |
		D3DXMESHOPT_COMPACT  |
		D3DXMESHOPT_VERTEXCACHE,
		(DWORD*)adjBuffer->GetBufferPointer(),
		0, 0, 0);

	//d3d::Release<ID3DXBuffer*>(adjBuffer); // done w/ buffer
	adjBuffer->Release();

	if(FAILED(hr))
	{
		MessageBox(0, "OptimizeInplace() - FAILED", 0, 0);
		return false;
	}

	//Add the first reference
	m_ref = 1;

	return true;
}

bool CDXDirectXMesh::UnloadMesh()
{
	if (m_mesh)
	{
		--m_ref;

		if (m_ref > 0)
			return true;

		m_mesh->Release();
		m_mesh = NULL;
		ZeroMemory(m_meshName, 16);
		m_isPrimitive = false;
		for (int i = 0; i < (int)m_textures.size(); ++i)
			CDXTextureManager::GetInstance()->ReleaseTexture(m_textures[i]);
		return true;
	}
	return false;
}

void CDXDirectXMesh::DrawMesh(IDirect3DDevice9* pDXDevice)
{
	if (m_mesh == 0)
		return;

	if (!m_isPrimitive)
	{
		CDXTextureManager* pTM = CDXTextureManager::GetInstance();

		for(unsigned int i = 0; i < m_materials.size(); i++)
		{
			pDXDevice->SetMaterial( &m_materials[i] );
			pDXDevice->SetTexture( 0, pTM->GetDXTextureData(m_textures[i]) );
			m_mesh->DrawSubset(i);
		}
	} 
	else
	{
		m_mesh->DrawSubset(0);
	}
}

void CDXDirectXMesh::DeleteMesh()
{
	UnloadMesh();
}

//////////////////////////////////////////////////////////////////////////

CDXUserMesh::CDXUserMesh() : IDXMesh(IDXMesh::MESHTYPE_USER_DEFINED)
{
	f_DrawFunc = NULL;
	f_DeleteFunc = NULL;
	m_vertices = NULL;
	m_texCoords = NULL;
	m_normals = NULL;
	m_triangles = NULL;
	m_vertexBuffer = NULL;
	m_indexBuffer = NULL;
}

CDXUserMesh::CDXUserMesh(DXMeshDrawPtr drawFuncPtr, DXMeshDelPtr deleteFuncPtr) : IDXMesh(IDXMesh::MESHTYPE_USER_DEFINED)
{
	f_DrawFunc = drawFuncPtr;
	f_DeleteFunc = deleteFuncPtr;
	m_vertices = NULL;
	m_texCoords = NULL;
	m_normals = NULL;
	m_triangles = NULL;
	m_vertexBuffer = NULL;
	m_indexBuffer = NULL;
}

CDXUserMesh::CDXUserMesh(const CDXUserMesh& rhs)
{

}

CDXUserMesh& CDXUserMesh::operator=(const CDXUserMesh& rhs)
{
	return *this;
}

CDXUserMesh::~CDXUserMesh()
{

}

void CDXUserMesh::DrawMesh(IDirect3DDevice9* pDXDevice)
{
	if (f_DrawFunc != NULL)
	{
		f_DrawFunc(pDXDevice);
	}
}

void CDXUserMesh::DeleteMesh()
{
	if (f_DeleteFunc != NULL)
	{
		--m_ref;

		if (m_ref == 0)
			f_DeleteFunc();
	}
}